import { Controller } from "egg";
import { createWriteStream } from "fs";
import { nanoid } from "nanoid";
import { extname, join, parse } from "path";
import * as sharp from "sharp";
import { pipeline } from "stream/promises";
import * as sendToWormhole from "stream-wormhole";
import * as Busboy from "busboy";
import { FileStream } from "../../typings/app";
import { createSSRApp } from "vue";
import { renderToNodeStream, renderToString } from "@vue/server-renderer";

export default class UtilsController extends Controller {
  splitIdAndUuid(str = "") {
    const result = { id: "", uuid: "" };
    if (!str) return result;
    const firstDashIndex = str.indexOf("-");
    if (firstDashIndex < 0) return result;
    result.id = str.slice(0, firstDashIndex);
    result.uuid = str.slice(firstDashIndex + 1);
    return result;
  }

  async renderH5Page() {
    const { ctx, app } = this;
    const { idAndUuid } = ctx.params;
    const query = this.splitIdAndUuid(idAndUuid);
    try {
      const pageData = await this.service.utils.renderToPageData(query);
      await ctx.render("page.nj", pageData);
    } catch (error) {
      ctx.helper.error({ ctx, errorType: "h5WorkNotExistError" });
    }
  }

  async fileLocalUpload() {
    const { ctx, app } = this;
    const { filepath } = ctx.request.files[0];
    // 生成shaarp实例
    const imageSource = sharp(filepath);
    const metaData = await imageSource.metadata();
    let thumbnailUrl = "";
    if (metaData.width && metaData.width > 300) {
      const { name, ext, dir } = parse(filepath);
      const thumbnailFilePath = join(dir, `${name}-thumbnail${ext}`);
      await imageSource.resize({ width: 300 }).toFile(thumbnailFilePath);
      thumbnailUrl = thumbnailFilePath.replace(app.config.baseDir, app.config.baseUrl);
    }
    const url = filepath.replace(app.config.baseDir, app.config.baseUrl);
    ctx.helper.success({ ctx, res: { url, thumbnailUrl: thumbnailUrl ? thumbnailUrl : url } });
  }

  pathToURL(path: string) {
    const { app } = this;
    return path.replace(app.config.baseDir, app.config.baseUrl);
  }

  async fileUploadByStream() {
    const { ctx, app } = this;
    const stream = await this.ctx.getFileStream();
    const uid = nanoid(6);
    const saveFilePath = join(app.config.baseDir, "uploads", uid + extname(stream.filename));
    const target = createWriteStream(saveFilePath);
    const savePromiss = pipeline(stream, target);

    const saveThumbnailPath = join(app.config.baseDir, "uploads", uid + "_thumbnail" + extname(stream.filename));
    const target2 = createWriteStream(saveThumbnailPath);
    const transformer = sharp().resize({ width: 300 });
    const thumbnailPromise = pipeline(stream, transformer, target2);

    try {
      await Promise.all([savePromiss, thumbnailPromise]);
    } catch (error) {
      return ctx.helper.error({ ctx, errorType: "imageUploadFail" });
    }
    ctx.helper.success({
      ctx,
      res: { url: this.pathToURL(saveFilePath), thumbnailUrl: this.pathToURL(saveThumbnailPath) },
    });
  }

  async uploadToOSS() {
    const { ctx, app } = this;
    const stream = await ctx.getFileStream();
    // logo-backend /imooc-test/**.ext
    const savedOSSPath = join("imooc-test", nanoid(6) + extname(stream.filename));
    try {
      const result = await ctx.oss.put(savedOSSPath, stream);
      app.logger.info(result);
      const { name, url } = result;
      ctx.helper.success({ ctx, res: { name, url } });
    } catch (e) {
      await sendToWormhole(stream);
      ctx.helper.error({ ctx, errorType: "imageUploadFail" });
    }
  }

  async testBusBoy() {
    const { app, ctx } = this;
    const results = await this.uploadFileUseBusBoy();
    ctx.helper.success({ ctx, res: results });
  }

  uploadFileUseBusBoy() {
    const { ctx, app } = this;
    return new Promise<string[]>((resolve, reject) => {
      const busboy = new Busboy({ headers: ctx.req.headers as any });
      const results: string[] = [];
      busboy.on("file", (fieldname, file, filename) => {
        app.logger.info(fieldname, file, filename);
        const uid = nanoid(6);
        const savedFilePath = join(app.config.baseDir, "uploads", uid + extname(filename));
        file.pipe(createWriteStream(savedFilePath));
        file.on("end", () => {
          results.push(savedFilePath);
        });
      });

      busboy.on("field", (fieldname, val) => {
        app.logger.info(fieldname, val);
      });

      busboy.on("finish", () => {
        app.logger.info("success");
        resolve(results);
      });
      ctx.req.pipe(busboy);
    });
  }

  async uploadMutipleFiles() {
    const { ctx, app } = this;
    const { fileSize } = app.config.multipart;
    const parts = ctx.multipart({ limits: { fileSize: fileSize as number } });
    const urls: string[] = [];
    let part: FileStream | string[];
    while ((part = await parts())) {
      if (Array.isArray(part)) {
        app.logger.info(part);
      } else {
        try {
          const savedOSSPath = join("imooc-test", nanoid(6) + extname(part.filename));
          const results = await ctx.oss.put(savedOSSPath, part);
          const { url } = results;
          urls.push(url);
          if (part.truncated) {
            await ctx.oss.delete(savedOSSPath);
            return ctx.helper.error({
              ctx,
              errorType: "imageUploadFileSizeError",
              error: `Reach fileSize limit ${fileSize} bytes`,
            });
          }
        } catch (error) {
          await sendToWormhole(part);
          ctx.helper.error({ ctx, errorType: "imageUploadFail" });
        }
      }
    }
    ctx.helper.success({ ctx, res: { urls } });
  }
}
